package com.itmake.sample.xss.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.itmake.sample.xss.wrapper.XssRequestWrapper;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * 防止 XSS 攻击的过滤器
 *
 * @author Amjacks
 * @since 2023/08/10
 */
public class XssFilter implements Filter {

    /**
     * 排除链接
     */
    private List<String> excludes = null;

    /**
     * 初始化参数
     */
    @Override
    public void init(FilterConfig filterConfig) {
        String tempExcludes = filterConfig.getInitParameter("excludes");
        if (CharSequenceUtil.isNotBlank(tempExcludes)) {
            excludes = new ArrayList<>();
            excludes.addAll(Arrays.asList(tempExcludes.split(",")));
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        // GET DELETE 不过滤
        String method = req.getMethod();
        if (Objects.isNull(method) || method.matches("GET") || method.matches("DELETE")) {
            chain.doFilter(request, response);
            return;
        }
        // 非 json 类型，不过滤
        String header = req.getHeader(HttpHeaders.CONTENT_TYPE);
        if (!CharSequenceUtil.startWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE)) {
            chain.doFilter(request, response);
            return;
        }
        // 排除链接
        String url = req.getServletPath();
        if (CharSequenceUtil.isNotBlank(url) && CollUtil.isNotEmpty(excludes)) {
            AntPathMatcher matcher = new AntPathMatcher();
            for (String exclude : excludes) {
                // 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
                if (matcher.match(exclude, url)) {
                    chain.doFilter(request, response);
                    return;
                }
            }
        }
        // 包装请求
        XssRequestWrapper xssRequest = new XssRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssRequest, response);
    }
}
